package com.redislock.aop.tool.aop;

import cn.hutool.core.util.StrUtil;
import com.redislock.aop.tool.annonation.ParamsificationAnnoation;
import com.redislock.aop.tool.annonation.RepeatAnnoation;
import com.redislock.aop.tool.bean.RedisLockDefinitionHolder;
import com.redislock.aop.tool.enums.RepeatEnums;
import com.redislock.aop.tool.exception.BusinessException;
import com.redislock.aop.tool.util.RedisUtil;
import com.redislock.aop.tool.util.ScheduleUtils;
import com.redislock.aop.tool.util.ToolUtils;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
@Log4j2
public class RepeatAop {
    @Resource
    private RedissonClient redissonClient;

    @Pointcut("@annotation(com.redislock.aop.tool.annonation.RepeatAnnoation)")
    public void repeat() {
    }

    private void continueProcessingJoinPoint(Boolean check) {
        if (!check) {
            throw new BusinessException("你已经执行完了，不可重复执行！");
        }
    }

    private Object handleProcessJoin(ProceedingJoinPoint proceedingJoinPoint) {
        try {
            return proceedingJoinPoint.proceed();
        } catch (Throwable e) {
            throw new BusinessException(e.getMessage());
        }
    }

    @Around("repeat()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
        RepeatAnnoation repeatAnnoation = method.getAnnotation(RepeatAnnoation.class);
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        if (repeatAnnoation.repeat() == RepeatEnums.Token) {
            String token = request.getHeader("request-token");
            //可以把项目里的访问用户名放到里面
            String user = "wangming";
            String key = ToolUtils.getTokenKey(user, token);
            continueProcessingJoinPoint(RedisUtil.del(key));
            return handleProcessJoin(proceedingJoinPoint);
        } else if (repeatAnnoation.repeat() == RepeatEnums.Param) {
            if (!method.isAnnotationPresent(ParamsificationAnnoation.class)) {
                throw new BusinessException("ParamsificationAnnoation不存在，请找管理员谢谢！");
            }
            //ip+类+办法名+用户名
            String user = "wangming";
            String key = StrUtil.format("order:submit:{}{}{}{}", request.getRemoteUser(), method.getDeclaringClass().getName(), method.getName(), user);
            RLock rLock = redissonClient.getLock(key);
            //若加锁成功的话，那么会返回true
            continueProcessingJoinPoint(rLock.tryLock(0, repeatAnnoation.lockTime(), TimeUnit.SECONDS));
            Object result;
            try {
                Long lastModifyTime = System.currentTimeMillis();
                Thread thread = Thread.currentThread();
                ParamsificationAnnoation paramsificationAnnoation = method.getAnnotation(ParamsificationAnnoation.class);
                RedisLockDefinitionHolder redisLockDefinitionHolder = new RedisLockDefinitionHolder(key, repeatAnnoation.lockTime(), lastModifyTime, thread, paramsificationAnnoation.tryCount());
                ScheduleUtils.redisLockDefinitionHolders.add(redisLockDefinitionHolder);
                result=handleProcessJoin(proceedingJoinPoint);
                while (thread.isInterrupted()) {
                    throw new BusinessException("线程已经中断了！");
                }
            } finally {
                //执行完毕后直接解锁下
                rLock.unlock();
            }
            return result;
        }
        return null;
    }
}
